package ee.serge.server;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.Transactional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import ee.serge.service.ControlPanelService;
import ee.serge.shared.Admin;
import ee.serge.shared.BusinessException;
import ee.serge.shared.Feedback;
import ee.serge.shared.HandledException;
import ee.serge.shared.IP;
import ee.serge.shared.IPFeedbackData;
import ee.serge.shared.Loan;
import ee.serge.shared.LoginException;

@SuppressWarnings("serial")
public class ControlPanelServiceImpl extends RemoteServiceServlet implements ControlPanelService {

   @Override
   public void registerAccount(Admin admin) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         try {
            Object o = pm.getObjectById(Admin.class, admin.getName());
            if (o != null) {
               throw new BusinessException("Username already registered");
            }
         } catch (Exception e) {}
         admin.setPassword(BCrypt.hashpw(admin.getPassword(), BCrypt.gensalt(12)));
         pm.makePersistent(admin);
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("Error creating account");
      } finally {
         pm.close();
      }
   }
   
   @Override
   public List<Admin> getAccountList() throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      List<Admin> returnAdmins = new ArrayList<Admin>();
      try {
         Query query = pm.newQuery(Admin.class);
         //query.setOrdering("name desc");
         List<Admin> admins = (List<Admin>) query.execute();
         admins.size();
         for (Admin admin : admins) {
         	returnAdmins.add(admin);
         }
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("System error: cannot get admins.");
      } finally {
         pm.close();
      }
      return returnAdmins;
   }

   @Override
   public boolean login(String name, String password) {
      PersistenceManager pm = PMF.get().getPersistenceManager();
      Admin admin = null;
      try {
         if (name.equals("serge") && password.equals("sibul")) {
            HttpServletRequest request = getThreadLocalRequest();
            HttpSession session = request.getSession();
            session.setAttribute("loggedin", true);
            return true;
         }
         Object object = pm.getObjectById(Admin.class, name);
         if (object == null) return false;
         admin = (Admin) object;
      } catch (Exception e) {
         return false;
      } finally {
         pm.close();
      }
      
      if (BCrypt.checkpw(password, admin.getPassword())) {
         HttpServletRequest request = getThreadLocalRequest();
         HttpSession session = request.getSession();
         session.setAttribute("loggedin", true);
         return true;
      }
      return false;     
   }
   
   @Override
   public void logout() {
      HttpServletRequest request = getThreadLocalRequest();
      HttpSession session = request.getSession();
      session.removeAttribute("loggedin");
   }

   @Override
   public boolean isLoggedIn() {
      HttpServletRequest request = getThreadLocalRequest();
      HttpSession session = request.getSession();
      Object attr = session.getAttribute("loggedin");
      try {
         if (attr != null && (Boolean)attr == true) {
            return true;
         }
      } catch (ClassCastException e) {}
      return false;
   }
   
   public void testLoggedIn() throws LoginException {
      HttpServletRequest request = getThreadLocalRequest();
      HttpSession session = request.getSession();
      Object attr = session.getAttribute("loggedin");
      try {
         if (attr != null && (Boolean)attr == true) {
            return;
         }
      } catch (ClassCastException e) {}
      throw new LoginException();
   }
   
   @Transactional
   @Override
   public List<Loan> getLoans() throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      List<Loan> returnLoans = new ArrayList<Loan>();
      try {
         Query query = pm.newQuery(Loan.class);
         query.setOrdering("creationTime desc");
         List<Loan> loans = (List<Loan>) query.execute();
         loans.size();
         for (Loan loan : loans) {
            returnLoans.add(loan);
         }
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("System error: cannot get loans.");
      } finally {
         pm.close();
      }
      return returnLoans;
   }

   @Override
   public void addLoan(Loan loan) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         if (loan.getCreationTime() == 0) {
            loan.setCreationTime(System.currentTimeMillis());
         }
         pm.makePersistent(loan);
      } catch (Exception e) {
         throw new BusinessException(e.getMessage());
      } finally {
         pm.close();
      }
   }

   @Override
   public Loan getLoan(String loanKey) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         Loan loan = pm.getObjectById(Loan.class, loanKey);
         pm.close();
         loan.setFeedbackList(getFeedbackList(loanKey));
         return loan;
      } catch (Exception e) {
         throw new BusinessException("Loan does not exist (key " + loanKey + ")");
      }
   }

	@Override
	public void removeLoan(String key) throws HandledException {
		testLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
	      Loan loan = pm.getObjectById(Loan.class, key);
	      pm.deletePersistent(loan);
      } catch (Exception e) {
	      throw new BusinessException("Error removing loan (key " + key + ")");
      } finally {
         pm.close();
      }
	}

	@Override
   public void removeAccount(String username) throws HandledException {
	   testLoggedIn();
	   PersistenceManager pm = PMF.get().getPersistenceManager();
	   try {
	      Admin admin = pm.getObjectById(Admin.class, username);
	      pm.deletePersistent(admin);
      } catch (Exception e) {
	      throw new BusinessException("Error removing account (username " + username + ")");
      } finally {
         pm.close();
      }
   }

   @Override
   public void saveLoan(Loan loan) throws HandledException {
      removeLoan(loan.getKey());
      addLoan(loan);
   }

   @Override
   public IPFeedbackData getFeedbackListWithIP(String ip) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      IPFeedbackData data = new IPFeedbackData();
      List<Feedback> returnList = new ArrayList<Feedback>();
      try {
         
         try {
            Object o = pm.getObjectById(IP.class, ip);
            if (o != null)
               data.setBanned(true);
            else 
               data.setBanned(false);
         } catch (Exception e) {data.setBanned(false);}
         
         Query query = pm.newQuery(Feedback.class);
         query.setFilter("ipAddress == A");
         query.setOrdering("creationTime desc");
         query.declareParameters("String A");
         List<Feedback> dbList = (List<Feedback>) query.execute(ip);
         dbList.size();
         for (Feedback val : dbList) {
            returnList.add(val);
         }
         data.setFeedbackList(returnList);
         data.setIp(ip);
         return data;
      } catch (Exception e) {
         System.out.println(e);
         System.out.println(e.getMessage());
         System.out.println(e.getStackTrace());
         throw new BusinessException("System error: cannot get reviews.");
      } finally {
         pm.close();
      }
   }

   @Override
   public List<Feedback> getFeedbackList(String loanKey) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      List<Feedback> returnList = new ArrayList<Feedback>();
      try {
         Query query = pm.newQuery(Feedback.class);
         query.setFilter("loanKey == A");
         query.setOrdering("creationTime desc");
         query.declareParameters("String A");
         List<Feedback> feedbacklist = (List<Feedback>) query.execute(loanKey);
         for (Feedback val : feedbacklist) {
            returnList.add(val);
         }
         return returnList;
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("System error.");
      } finally {
         pm.close();
      }
   }

   @Override
   public void removeFeedback(String key) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         Feedback feedback = pm.getObjectById(Feedback.class, key);
         pm.deletePersistent(feedback);
         Loan loan = pm.getObjectById(Loan.class, feedback.getLoanKey());
         loan.decreaseFeedbackCount();
         loan.increaseTotalRating(-1 * feedback.getRating());
      } catch (Exception e) {
         throw new BusinessException("Error removing review (key " + key + ")");
      } finally {
         pm.close();
      }
   }

   @Override
   public void banIP(String ipStr) throws HandledException {
      testLoggedIn();
      IP ip = new IP(ipStr);
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         try {
            Object o = pm.getObjectById(IP.class, ip.getIp());
            if (o != null) {
               throw new BusinessException("Ban already exists");
            }
         } catch (Exception e) {}
         pm.makePersistent(ip);
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("Error banning IP");
      } finally {
         pm.close();
      }
   }

   @Override
   public boolean isBanned(String ipStr) throws HandledException {
      testLoggedIn();
      IP ip = new IP(ipStr);
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         try {
            Object o = pm.getObjectById(IP.class, ip.getIp());
            if (o != null) {
               return true;
            }
         } catch (Exception e) {}
      } catch (Exception e) {
         System.out.println(e);
         throw new BusinessException("Error checking for IP");
      } finally {
         pm.close();
      }
      return false;
   }

   @Override
   public void removeBan(String ipStr) throws HandledException {
      testLoggedIn();
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         IP ip = pm.getObjectById(IP.class, ipStr);
         pm.deletePersistent(ip);
      } catch (Exception e) {
          throw new BusinessException("Error removing IP (" + ipStr + ")");
      } finally {
         pm.close();
      }
   }

   @Override
   public void addFeedback(Feedback feedback) throws HandledException {
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try {
         Object o = pm.getObjectById(IP.class, getThreadLocalRequest().getRemoteAddr());
         if (o != null) {
            throw new BusinessException("You are banned, you cannot post a review.");
         }
      } catch (JDOException e) {}
      try {
         feedback.setIpAddress(getThreadLocalRequest().getRemoteAddr());
         if (feedback.getCreationTime() == 0) {
            feedback.setCreationTime(System.currentTimeMillis());
         }
         pm.makePersistent(feedback);
      } catch (JDOException e) {
         throw new BusinessException("Error submitting review.");
      } finally {
         pm.close();
      }
   }

}
